package com.flca.frw.config;

import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.flca.frw.util.FileUtils;
import com.flca.frw.util.ShellUtils;
import com.flca.frw.util.SystemVarUtils;

/**
 * Helper class to collect all resources available from the given folders Either
 * a file-system root folder, or a classpath folder.
 */
public class ConfigFilesList {

	private static Logger logger = LoggerFactory.getLogger(ConfigFilesList.class);
	
	private File mInputRootFolder;
	private String mUseRootFolder;
	private FilePatternMatcher mPattern;

	/**
	 * ctor for this helper class that will collect all relevant files (ini xml
	 * and.or properties) under the given config-root folder it depends what
	 * method is called later if the files are searched for under the classpath
	 * or the file-system
	 * 
	 * @param mRootFolder
	 * @param mPattern
	 */
	public ConfigFilesList(File aRootFolder, FilePatternMatcher aPattern) {
		super();
		mInputRootFolder = aRootFolder;
		mUseRootFolder = getUseRootFolder(aRootFolder);
		logger.debug("aRootFolder" + aRootFolder + "  mUseRootFolder=" + mUseRootFolder);
		mPattern = aPattern;
	}

	/**
	 * return all relevant files under the classpath
	 */
	public Collection<String> getResources() {
		final ArrayList<String> retval = new ArrayList<String>();
		final String classPath = System.getProperty("java.class.path", ".");
		final String[] classPathElements = classPath.split(getClassPathDelim());
		for (final String element : classPathElements) {
			retval.addAll(getFiles(element, true));
		}
		return retval;
	}

	/**
	 * return all relevant files under the rootfolder from the file-system
	 * 
	 * @return
	 */
	public Collection<String> getFiles() {
		final ArrayList<String> retval = new ArrayList<String>();
		retval.addAll(getFiles(mUseRootFolder, false));
		return retval;
	}

	private Collection<String> getFiles(final String element, boolean aForResources) {
		final ArrayList<String> retval = new ArrayList<String>();
		final File file = new File(element);
		if (file.isDirectory()) {
			retval.addAll(getResourcesFromDirectory(file, aForResources));
		}
		return retval;
	}

	private Collection<String> getResourcesFromDirectory(final File directory, boolean aForResources) {
		final ArrayList<String> retval = new ArrayList<String>();
		final File[] fileList = directory.listFiles();
		for (final File file : fileList) {
			if (file.isDirectory()) {
				if (isValidFolder(file.getPath(), aForResources)) {
					retval.addAll(getTheResources(file));
				}
			} else {
				try {
					final String fileName = file.getCanonicalPath();
					final boolean accept = mPattern.matches(fileName);
					if (accept && isValidFolder(fileName, aForResources)) {
						retval.add(fileName);
					}
				} catch (final IOException e) {
					throw new Error(e);
				}
			}
		}
		return retval;
	}

	private Collection<String> getTheResources(File aRoot) {
		return FileUtils.findFiles(aRoot.getPath(), new ConfigFilesFilter(mPattern), true);
	}
	
	/*
	 * return the rootfolder and take into account that name may contain a
	 * system property
	 */
	private String getUseRootFolder(File aInputRootFolder) {
		String result = mInputRootFolder.getPath();

		if (result.startsWith("$") || result.startsWith("%")) {
			result = SystemVarUtils.replaceSystemVariables(result);
		}

		return FileUtils.formatPath(result);
	}

	/*
	 * this method checks if the folder is one the classpath files
	 */
	private boolean isValidFolder(String aFilename, boolean aForResources) {
		logger.debug("checking : " + aFilename + " " + mUseRootFolder);
		return FileUtils.formatPath(aFilename).endsWith(mUseRootFolder);
			//TODO check more carefully
	}

	private String getClassPathDelim() {
		return (ShellUtils.isLinux()) ? ":" : ";";
	}
	
	//-------------------
	
	private static class ConfigFilesFilter implements FileFilter {

		FilePatternMatcher pattern;
		
		public ConfigFilesFilter(FilePatternMatcher pattern) {
			super();
			this.pattern = pattern;
		}


		@Override
		public boolean accept(File pathname) {
			if (pathname.isDirectory()) {
				return true;
			} else {
				boolean b = pattern.matches(pathname.getPath());
				if (b) {
					System.out.println(pathname);
				}
				return b;
			}
		}
		
	}

}